param(
    [string]$TinyGo = "tinygo",
    [string]$Nasm = "nasm",
    [string]$Linker = "ld.lld",
    [string]$Objcopy = "llvm-objcopy"
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

$root = Resolve-Path (Join-Path $PSScriptRoot "..")
$buildDir = Join-Path $root "build"
if (-not (Test-Path $buildDir)) {
    New-Item -ItemType Directory -Path $buildDir | Out-Null
}

$kernelObj = Join-Path $buildDir "kernel.o"
$kernelEntryObj = Join-Path $buildDir "kernel_entry.o"
$portIOObj = Join-Path $buildDir "port_io.o"
$trampObj = Join-Path $buildDir "trampoline.o"
$interruptsObj = Join-Path $buildDir "interrupts.o"
$contextObj = Join-Path $buildDir "context.o"
$kernelElf = Join-Path $buildDir "kernel.elf"
$kernelBin = Join-Path $buildDir "kernel.bin"
$kernelPad = Join-Path $buildDir "kernel.padded.bin"
$bootBin = Join-Path $buildDir "boot.bin"
$osImg = Join-Path $buildDir "os.img"

$targetFile = Join-Path $root "targets\tinygo-i386.json"
$kernelDir = Join-Path $root "cmd\kernel"

# Build sample ELF programs first so go:embed can include fresh binaries
$helloBuild = Join-Path $root "scripts\build-hello.ps1"
if (Test-Path $helloBuild) {
    Write-Host "Building sample programs via $helloBuild"
    & powershell -ExecutionPolicy Bypass -File $helloBuild
    if ($LASTEXITCODE -ne 0) {
        throw "Sample program build failed"
    }
}

# Build TinyGo user program samples and install into assets
$goSamplesBuild = Join-Path $root "scripts\build-go-samples.ps1"
if (Test-Path $goSamplesBuild) {
    Write-Host "Building TinyGo samples via $goSamplesBuild"
    & powershell -ExecutionPolicy Bypass -File $goSamplesBuild
    if ($LASTEXITCODE -ne 0) {
        throw "TinyGo samples build failed"
    }
}

& $TinyGo build -o $kernelObj -target $targetFile -scheduler "none" -panic "trap" -no-debug $kernelDir
if ($LASTEXITCODE -ne 0) {
    throw "TinyGo compilation failed"
}

$kernelEntryAsm = Join-Path $root "boot\kernel_entry.asm"
& $Nasm "-f" "elf32" "-o" $kernelEntryObj $kernelEntryAsm
if ($LASTEXITCODE -ne 0) {
    throw "NASM failed on kernel entry"
}

$portIOAsm = Join-Path $root "boot\port_io.asm"
& $Nasm "-f" "elf32" "-o" $portIOObj $portIOAsm
if ($LASTEXITCODE -ne 0) {
    throw "NASM failed on port I/O"
}

$trampAsm = Join-Path $root "boot\trampoline.asm"
& $Nasm "-f" "elf32" "-o" $trampObj $trampAsm
if ($LASTEXITCODE -ne 0) {
    throw "NASM failed on trampoline"
}

$interruptsAsm = Join-Path $root "boot\interrupts.asm"
& $Nasm "-f" "elf32" "-o" $interruptsObj $interruptsAsm
if ($LASTEXITCODE -ne 0) {
    throw "NASM failed on interrupts"
}

$contextAsm = Join-Path $root "boot\context.asm"
& $Nasm "-f" "elf32" "-o" $contextObj $contextAsm
if ($LASTEXITCODE -ne 0) {
    throw "NASM failed on context"
}
& $Nasm "-f" "elf32" "-o" $trampObj $trampAsm
if ($LASTEXITCODE -ne 0) {
    throw "NASM failed on trampoline"
}

$linkerScript = Join-Path $root "linker.ld"
& $Linker "-m" "elf_i386" "-T" $linkerScript "-o" $kernelElf $kernelEntryObj $portIOObj $trampObj $interruptsObj $contextObj $kernelObj
if ($LASTEXITCODE -ne 0) {
    throw "Linker failed"
}

& $Objcopy "-O" "binary" $kernelElf $kernelBin
if ($LASTEXITCODE -ne 0) {
    throw "llvm-objcopy failed"
}

$kernelBytes = [System.IO.File]::ReadAllBytes($kernelBin)
$padding = (512 - ($kernelBytes.Length % 512)) % 512
$kernelList = New-Object System.Collections.Generic.List[byte]
$kernelList.AddRange($kernelBytes)
if ($padding -gt 0) {
    $pad = New-Object byte[] $padding
    $kernelList.AddRange($pad)
}
$kernelPaddedBytes = $kernelList.ToArray()
[System.IO.File]::WriteAllBytes($kernelPad, $kernelPaddedBytes)

$kernelSectors = [int][Math]::Ceiling($kernelPaddedBytes.Length / 512.0)

$bootAsm = Join-Path $root "boot\boot.asm"
& $Nasm "-f" "bin" "-o" $bootBin "-D" "KERNEL_SECTORS=$kernelSectors" $bootAsm
if ($LASTEXITCODE -ne 0) {
    throw "Boot sector assembly failed"
}

$bootBytes = [System.IO.File]::ReadAllBytes($bootBin)
$combined = New-Object byte[] ($bootBytes.Length + $kernelPaddedBytes.Length)
[System.Array]::Copy($bootBytes, 0, $combined, 0, $bootBytes.Length)
[System.Array]::Copy($kernelPaddedBytes, 0, $combined, $bootBytes.Length, $kernelPaddedBytes.Length)
[System.IO.File]::WriteAllBytes($osImg, $combined)

Write-Host "Built raw image at $osImg"
